#include "udpclient.hpp"

#include <sstream>
#include <string>
#include <unistd.h>

#include "topconn_string.hpp"
#include "topconn_error.hpp"

#include "protocal/udp/udphelper.hpp"

namespace topconn
{
#define maxBufSize      8192

    UdpClient::UdpClient()
        : m_sockFd(-1)
        , m_port(0)
        , m_maxTimeout(30)
        , m_isBlock(true)
        , m_isIpv6(false)
    {
    }

    UdpClient::UdpClient(const string& addr, uint16_t port)
        : m_sockFd(-1)
        , m_addr(addr)
        , m_port(port)
        , m_maxTimeout(30)
        , m_isBlock(true)
        , m_isIpv6(false)
    {
    }

    UdpClient::UdpClient(const string& addr, uint16_t port, uint16_t timeout)
        : m_sockFd(-1)
        , m_addr(addr)
        , m_port(port)
        , m_maxTimeout(timeout)
        , m_isBlock(true)
        , m_isIpv6(false)
    {
    }

    UdpClient::~UdpClient()
    {
        close();
    }

    void UdpClient::setAddr(const string& addr, uint16_t port)
    {
        m_addr = addr;
        m_port = port;
    }

    void UdpClient::setMaxTimeout(uint16_t maxTimeout)
    {
        m_maxTimeout = maxTimeout;
    }

    void UdpClient::setBlock(bool on)
    {
        m_isBlock = on;
    }

    void UdpClient::setIpv6(bool on)
    {
        m_isIpv6 = on;
    }

    const string& UdpClient::getAddr() const
    {
        return m_addr;
    }

    uint16_t UdpClient::getPort() const
    {
        return m_port;
    }

    uint16_t UdpClient::getMaxTimeout() const
    {
        return m_maxTimeout;
    }

    bool UdpClient::isBlock() const
    {
        return m_isBlock;
    }

    bool UdpClient::isIpv6() const
    {
        return m_isIpv6;
    }

    int UdpClient::create()
    {
        m_sockFd = UdpHelper::create(m_isBlock, m_isIpv6);
        if (m_sockFd < 0)
        {
            return CREATE_SOCKET_ERROR;
        }
        return SUCESSED;
    }

    void UdpClient::close()
    {
        if (m_sockFd > 0)
        {
            ::close(m_sockFd);
            m_sockFd = -1;
        }
    }

    int UdpClient::sendto(const string& sndbuf)
    {
        int iRet = 0;
        int sendLen = 0;
        unsigned int toLen;
        struct sockaddr_in toAddr;
        memset(&toAddr, 0x00, sizeof(toAddr));

        string ssndBuf(sndbuf);
        if (m_sockFd < 0)
        {
            return INVALID_SOCKET;
        }

        toAddr.sin_family = AF_INET;
        toAddr.sin_port = htons(m_port);
        if (inet_pton(AF_INET, m_addr.c_str(), &toAddr.sin_addr) <= 0)
        {
            uint32_t ret = SockUtil::getHostByName(m_addr);
            if (ret == uint32_t(-1))
            {
                return SEND_ERROR;
            }
            toAddr.sin_addr.s_addr = ret;
        }
        toLen = sizeof(toAddr);
        if ((sendLen = ::sendto(m_sockFd, ssndBuf.c_str(), ssndBuf.length(), 0, (struct sockaddr*)&toAddr, toLen)) < 0)
        {
            printf("sendto() error:%s\n", strerror(errno));
            return SEND_ERROR;
        }
        return SUCESSED;
    }

    int UdpClient::recvfrom(string& rcvbuf)
    {
        int iRet = 0;
        if (m_sockFd < 0)
        {
            return INVALID_SOCKET;
        }

        iRet = SockUtil::isReadAble(m_sockFd, m_maxTimeout);
        if (iRet < 0)
            return OTHER_ERROR;
        else if (iRet == 0)
            return RECV_TIMEOUT;

        char recvBuf[maxBufSize] = { 0 };
        int recvLen = ::recvfrom(m_sockFd, recvBuf, sizeof(recvBuf), 0, NULL, NULL);
        if ( recvLen <= 0)
        {
            return RECV_ERROR;
        }
        rcvbuf = string(recvBuf, recvLen);
        return SUCESSED;
    }
};